package org.xiaov.lunasql.engine.transform.oracle;

import java.util.HashMap;
import java.util.Map;

/**
 * DateFormatConverter
 *
 * @author lunasql
 * @since 2024/12/19
 */
public class DateFormatConverter {

    // Mapping of MySQL format specifiers to Oracle format models
    private static final Map<String, String> MYSQL_TO_ORACLE_MAP = new HashMap<>();
    // Mapping of Oracle format models to MySQL format specifiers
    private static final Map<String, String> ORACLE_TO_MYSQL_MAP = new HashMap<>();

    static {
        MYSQL_TO_ORACLE_MAP.put("%Y", "YYYY");
        MYSQL_TO_ORACLE_MAP.put("%y", "YY");
        MYSQL_TO_ORACLE_MAP.put("%m", "MM");
        MYSQL_TO_ORACLE_MAP.put("%c", "MON"); // Note: This will give the abbreviated month name in Oracle
        MYSQL_TO_ORACLE_MAP.put("%d", "DD");
        MYSQL_TO_ORACLE_MAP.put("%e", "DD");  // Oracle doesn't have a direct equivalent for single-digit day without leading zero
        MYSQL_TO_ORACLE_MAP.put("%H", "HH24");
        MYSQL_TO_ORACLE_MAP.put("%h", "HH12");
        MYSQL_TO_ORACLE_MAP.put("%I", "HH12");
        MYSQL_TO_ORACLE_MAP.put("%i", "MI");
        MYSQL_TO_ORACLE_MAP.put("%S", "SS");
        MYSQL_TO_ORACLE_MAP.put("%s", "SS");
        MYSQL_TO_ORACLE_MAP.put("%W", "DAY");
        MYSQL_TO_ORACLE_MAP.put("%a", "DY");
        MYSQL_TO_ORACLE_MAP.put("%p", "AM");

        // Populate the reverse map for Oracle to MySQL conversion
        MYSQL_TO_ORACLE_MAP.forEach((mysql, oracle) -> ORACLE_TO_MYSQL_MAP.put(oracle, mysql));
    }

    /**
     * Converts MySQL DATE_FORMAT string to Oracle TO_CHAR format model.
     *
     * @param mysqlFormat The MySQL format string to convert.
     * @return The converted Oracle format model string.
     */
    public static String mysqlToOracleDateFormat(String mysqlFormat) {
        StringBuilder oracleFormat = new StringBuilder();
        int length = mysqlFormat.length();
        for (int i = 0; i < length; ) {
            if (i + 1 < length && mysqlFormat.charAt(i) == '%' && MYSQL_TO_ORACLE_MAP.containsKey(mysqlFormat.substring(i, i + 2))) {
                oracleFormat.append(MYSQL_TO_ORACLE_MAP.get(mysqlFormat.substring(i, i + 2)));
                i += 2; // Skip the next character as it's part of the specifier
            } else {
                // Handle literal characters directly
                oracleFormat.append(mysqlFormat.charAt(i));
                i++;
            }
        }
        return oracleFormat.toString();
    }

    /**
     * Converts Oracle TO_CHAR format model to MySQL DATE_FORMAT string.
     *
     * @param oracleFormat The Oracle format model string to convert.
     * @return The converted MySQL format string.
     */
    public static String oracleToMysqlDateFormat(String oracleFormat) {
        StringBuilder mysqlFormat = new StringBuilder();
        int length = oracleFormat.length();
        boolean inQuote = false;
        for (int i = 0; i < length; i++) {
            char currentChar = oracleFormat.charAt(i);

            if (currentChar == '\'') {
                inQuote = !inQuote;
                continue;
            }

            if (!inQuote && ORACLE_TO_MYSQL_MAP.containsKey(Character.toString(currentChar))) {
                mysqlFormat.append('%').append(ORACLE_TO_MYSQL_MAP.get(Character.toString(currentChar)));
            } else if (!inQuote && i + 2 < length && ORACLE_TO_MYSQL_MAP.containsKey(oracleFormat.substring(i, i + 3))) {
                mysqlFormat.append(ORACLE_TO_MYSQL_MAP.get(oracleFormat.substring(i, i + 3)));
                i += 2; // Skip the next two characters as they're part of the specifier
            } else if (!inQuote && i + 1 < length && ORACLE_TO_MYSQL_MAP.containsKey(oracleFormat.substring(i, i + 2))) {
                mysqlFormat.append(ORACLE_TO_MYSQL_MAP.get(oracleFormat.substring(i, i + 2)));
                i++; // Skip the next character as it's part of the specifier
            } else {
                // Handle literal characters directly
                mysqlFormat.append(currentChar);
            }
        }
        return mysqlFormat.toString();
    }

}